#include "NodeVisitor.h"

using namespace std;

void EnvironmentBuildingVisitor::visit(RootNode*n){
	n->class_environment = class_environment;
}
void EnvironmentBuildingVisitor::visit(CompilationUnitNode*n){
	error |= !class_environment[current_package_name].insert((ClassDeclarationNode*)current_class).second;

	current_package_name = string();
}
void EnvironmentBuildingVisitor::visit(PackageDeclarationNode*n){
	current_package_name = n->identifier;
}
void EnvironmentBuildingVisitor::visit(ClassDeclarationNode*n){

	for (vector<TypedNode*>::iterator it = current_methods.begin(); it < current_methods.end(); ++it) {
		error |= !n->elements_in_scope[DeclarationNode::Method].insert(*it).second;
	}
	for (vector<TypedNode*>::iterator it = current_fields.begin(); it < current_fields.end(); ++it) {
		error |= !n->elements_in_scope[DeclarationNode::Expression].insert(*it).second;
	}

	current_methods.clear();
	current_fields.clear();

	n->package_name = current_package_name;
	current_class = n;
}
void EnvironmentBuildingVisitor::visit(InterfaceDeclarationNode*n){
	for (vector<TypedNode*>::iterator it = current_methods.begin(); it < current_methods.end(); ++it) {
		error |= !n->elements_in_scope[DeclarationNode::Method].insert(*it).second;
	}
	current_methods.clear();

	n->package_name = current_package_name;
	current_class = n;
}
void EnvironmentBuildingVisitor::visit(ConstructorDeclarationNode*n){
	for (set<TypedNode*, compare_decls>::iterator it = current_params.begin(); it != current_params.end(); ++it) {
		n->elements_in_scope[DeclarationNode::Expression].insert(*it);
	}

	current_params.clear();

	current_methods.push_back(n);

}
void EnvironmentBuildingVisitor::visit(MethodDeclarationNode*n){
	for (set<TypedNode*, compare_decls>::iterator it = current_params.begin(); it != current_params.end(); ++it) {
		n->elements_in_scope[DeclarationNode::Expression].insert(*it);
	}

	current_params.clear();

	current_methods.push_back(n);
}
void EnvironmentBuildingVisitor::visit(AbstractMethodDeclarationNode*n){
	for (set<TypedNode*, compare_decls>::iterator it = current_params.begin(); it != current_params.end(); ++it) {
		n->elements_in_scope[DeclarationNode::Expression].insert(*it);
	}

	current_params.clear();
	current_methods.push_back(n);
}
void EnvironmentBuildingVisitor::visit(FieldDeclarationNode*n){
	current_fields.push_back(n);
}
void EnvironmentBuildingVisitor::visit(FormalParameterNode*n){
	error |= !current_params.insert(n).second;
}
void EnvironmentBuildingVisitor::visit(LocalVariableDeclarationNode*n){

	//We need to be greedy here and swim to the nearest environment and insert right now
	ParentNode *p = n->parent;
	for (; p->Type() != "Block"; p = p->parent);

	error |= !((BlockNode*)p)->elements_in_scope[DeclarationNode::Expression].insert(n).second;
}

/* THIS ISN'T ACTUALLY FOR ENVIRONMENT BUILDING. THIS IS JUST A CONVENIENT POINT TO CHECK FOR DECLARATION BEFORE USE */
void EnvironmentBuildingVisitor::visit(NameNode *n) {
	string local_name = ((LeafNode*)(*n)[0]())->lexeme;
	TypedNode dn;
	dn.identifier = local_name;

	bool is_left_hand = n->parent->Type() == "LeftHandSide";

	ParentNode *p = n;
	while (p) {
		//Swim up the blocks looking for local variables, but also stop off and find out if you're something like [int i = i = 3;] which the 2nd 'i' is fine
		for (p = p->parent; p && p->Type() != "Block"; p = p->parent) {
			//The base we're looking for is actually what's being declared right now
			if (p->Type() == "LocalVariableDeclaration" && ((LocalVariableDeclarationNode*)p)->identifier == local_name) {
				//If it was used in the left hand side of an assignment (like int i = i = 3;) and it's what's being declared, we're okay.
				if (is_left_hand) {
					n->declaration_namespace = DeclarationNode::Expression;
					n->declaration_reference = ((LocalVariableDeclarationNode*)p);

				//Otherwise, it's not in an LHS, and we're breaking forward declaration
				} else error = true;
				return;
			}
		}
		
		if (!p) break;

		BlockNode *b = (BlockNode*)p;
		set<TypedNode*, compare_decls>::iterator local_find_result = b->elements_in_scope[DeclarationNode::Expression].find(&dn);
		if (local_find_result != b->elements_in_scope[DeclarationNode::Expression].end()) {
			n->declaration_namespace = DeclarationNode::Expression;
			n->declaration_reference = *local_find_result;
			return;
		}
	}
}

void EnvironmentBuildingVisitor::visit(BlockNode *n) {
	if (n->elements_in_scope.find(DeclarationNode::Expression) == n->elements_in_scope.end()) return;

	set<TypedNode*, compare_decls> locals = n->elements_in_scope[DeclarationNode::Expression];
	for (set<TypedNode*, compare_decls>::iterator it = locals.begin(); it != locals.end(); ++it) {
		if (current_params.find(*it) != current_params.end()) {
			error = true;
			return;
		}

		ParentNode *p = n;
		while (p) {
			for (p = p->parent; p && p->Type() != "Block"; p = p->parent);
			if (p) {
				set<TypedNode*, compare_decls> exprs = ((BlockNode*)p)->elements_in_scope[DeclarationNode::Expression];
				error |= exprs.find(*it) != exprs.end();
				if (error) return;
			}
		}
	}
}